/* pidwidget.cpp
 * Class PIDwidget inherits QDockWidget
 * This is the PID inspector widget
 * Created on startup
 * Mostly quite simple -  just writing the relevant registers when the UI is changed
 */

#include <QComboBox>
#include <QLineEdit>
#include <QCheckBox>

#include "pidwidget.h"

PIDwidget::PIDwidget(QWidget *parent) : QDockWidget(parent)
{
    //Really this should be static const ... but I couldn't find out how to initialise a
    // static const QStringList
    pidRegNames = (QStringList() << "PID_CTRL" << "PID_PERIOD" <<
                                            "PID_SRC" << "PID_DEST" << "PID_REF" << "PID_PGAIN" <<
                                            "PID_IGAIN" << "PID_DGAIN" << "PID_KSTART" <<
                                            "PID_LIMIT_LOW" << "PID_LIMIT_HIGH");
    m_reglist = 0;
}

void PIDwidget::setRegisterList(QList<RegisterDef> *regList)
{
    /* note: finding ui elements and connecting the signals/slots is NOT done during the constructor
     * this widget is created BEFORE its children, so has to wait to find them
     */

    m_reglist = regList;

    //Used to ensure you can only enter valid numbers in the QLineEdits
    QDoubleValidator *validator = new QDoubleValidator(this);

    //Find all the ui elements and give them names
    ui_pidEnabled = qFindChild<QCheckBox*>(this, "pidEnabled");
    ui_pidChannel = qFindChild<QComboBox*>(this, "pidChannel");
    ui_pidSrc = qFindChild<QComboBox*>(this, "pidSrc");
    ui_pidDest = qFindChild<QComboBox*>(this, "pidDest");
    ui_pidRef = qFindChild<QLineEdit*>(this, "pidRef");
    ui_pidRef->setValidator(validator);
    ui_pidMax = qFindChild<QLineEdit*>(this, "pidMax");
    ui_pidMax->setValidator(validator);
    ui_pidMin = qFindChild<QLineEdit*>(this, "pidMin");
    ui_pidMin->setValidator(validator);
    ui_pidPgain = qFindChild<QLineEdit*>(this, "pidPgain");
    ui_pidPgain->setValidator(validator);
    ui_pidIgain = qFindChild<QLineEdit*>(this, "pidIgain");
    ui_pidIgain->setValidator(validator);
    ui_pidDgain = qFindChild<QLineEdit*>(this, "pidDgain");
    ui_pidDgain->setValidator(validator);
    ui_pidKterm = qFindChild<QLineEdit*>(this, "pidKstart");
    ui_pidKterm->setValidator(validator);
    ui_pidInterval = qFindChild<QLineEdit*>(this, "pidInterval");
    ui_pidInterval->setValidator(validator);

    connect(ui_pidChannel, SIGNAL(currentIndexChanged(int)), this, SLOT(on_pidChannel_currentIndexChanged(int)));
    connect(ui_pidEnabled, SIGNAL(stateChanged(int)), this, SLOT(on_pidEnabled_stateChanged(int)));
    connect(ui_pidSrc, SIGNAL(activated(int)), this, SLOT(on_pidSrc_activated(int)));
    connect(ui_pidDest, SIGNAL(activated(int)), this, SLOT(on_pidDest_activated(int)));
    connect(ui_pidRef, SIGNAL(editingFinished()), this, SLOT(on_pidRef_editingFinished()));
    connect(ui_pidMax, SIGNAL(editingFinished()), this, SLOT(on_pidMax_editingFinished()));
    connect(ui_pidMin, SIGNAL(editingFinished()), this, SLOT(on_pidMin_editingFinished()));
    connect(ui_pidPgain, SIGNAL(editingFinished()), this, SLOT(on_pidPgain_editingFinished()));
    connect(ui_pidIgain, SIGNAL(editingFinished()), this, SLOT(on_pidIgain_editingFinished()));
    connect(ui_pidDgain, SIGNAL(editingFinished()), this, SLOT(on_pidDgain_editingFinished()));
    connect(ui_pidKterm, SIGNAL(editingFinished()), this, SLOT(on_pidKterm_editingFinished()));
    connect(ui_pidInterval, SIGNAL(editingFinished()), this, SLOT(on_pidInterval_editingFinished()));
}

void PIDwidget::loadPIDinfo()
{
    //Fill up the PID Channel selector with the values 1-8
    for (register int i=1; i < 9; ++i) {
        ui_pidChannel->addItem(QString::number(i));
    }

    //Queue a read of the PID_SRCS and PID_DESTS registers so we can populate the comboboxes
    RegisterDef reg = RegisterDef("PID_SRCS");
    if (m_reglist->contains(reg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        emit readNeeded(reg.address());
    }
    reg = RegisterDef("PID_DESTS");
    if (m_reglist->contains(reg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        emit readNeeded(reg.address());
    }

    //Then queue a read of all the other relevant registers
    updatePIDinfo();
}

void PIDwidget::updatePIDinfo()
{
    RegisterDef reg;
    foreach (QString regName, pidRegNames) {
        reg = RegisterDef(regName);
        if (m_reglist->contains(reg)) {
            reg = m_reglist->at(m_reglist->indexOf(reg));
            emit readNeeded(reg.address());
        }
    }
}

void PIDwidget::on_pidChannel_currentIndexChanged(int index)
{
    //If the user selects a different PID channel let the device know
    // read the values back from the device
    RegisterDef reg = RegisterDef("PID_CHANNEL");
    if (m_reglist->contains(reg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        emit writeNeeded(reg.address(), QString::number(index));
        updatePIDinfo();
    }
}

void PIDwidget::pidRegisterUpdated(RegisterDef reg)
{
    //Whenever a register is updated check whether it is relevant to the PID
    // if it is set the UI appropriately
    QString rawPidSrc;
    QString rawPidDest;
    RegisterDef pidReg;

    //If it was PID_SRCS or PID_DESTS repopulate the combo box
    if (reg.name() == "PID_SRCS") {
        if (m_reglist->contains(reg)) {
            ui_pidSrc->clear();
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            pidSrcs = pidReg.getValue().split(",");
            pidSrcs.prepend("<none selected>");
            ui_pidSrc->addItems(pidSrcs);
        }
        return;
    }

    if (reg.name() == "PID_DESTS") {
        if (m_reglist->contains(reg)) {
            ui_pidDest->clear();
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            pidDests = pidReg.getValue().split(",");
            pidDests.prepend("<none selected>");
            ui_pidDest->addItems(pidDests);
        }
        return;
    }

    if (reg.name() == "PID_CHANNEL") {
        if (m_reglist->contains(reg)) {
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            ui_pidChannel->setCurrentIndex(pidReg.iValue());
        }
        return;
    }

    if (reg.name() == "PID_CTRL") {
        if (m_reglist->contains(reg)) {
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            if (pidReg.iValue() == 0) {
                ui_pidEnabled->setChecked(false);
            } else {
                ui_pidEnabled->setChecked(true);
            }
        }
        return;
    }

    if (reg.name() == "PID_PERIOD") {
        if (m_reglist->contains(reg)) {
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            ui_pidInterval->setText(pidReg.getValue());
        }
        return;
    }

    if (reg.name() == "PID_SRC") {
        if (m_reglist->contains(reg)) {
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            RegisterDef targetReg = RegisterDef(pidReg.iValue());
            if (m_reglist->contains(targetReg)) {
                targetReg = m_reglist->at(m_reglist->indexOf(targetReg));
                int index = pidSrcs.indexOf(targetReg.name());
                if (index < 0)
                    index = 0;
                ui_pidSrc->setCurrentIndex(index);
            }
        }
        return;
    }

    if (reg.name() == "PID_DEST") {
        if (m_reglist->contains(reg)) {
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            RegisterDef targetReg = RegisterDef(pidReg.iValue());
            if (m_reglist->contains(targetReg)) {
                targetReg = m_reglist->at(m_reglist->indexOf(targetReg));
                int index = pidDests.indexOf(targetReg.name());
                if (index < 0)
                    index = 0;
                ui_pidDest->setCurrentIndex(index);
            }
        }
    }

    if (reg.name() == "PID_REF") {
        if (m_reglist->contains(reg)) {
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            ui_pidRef->setText(pidReg.getValue());
        }
        return;
    }

    if (reg.name() == "PID_PGAIN") {
        if (m_reglist->contains(reg)) {
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            ui_pidPgain->setText(pidReg.getValue());
        }
        return;
    }

    if (reg.name() == "PID_IGAIN") {
        if (m_reglist->contains(reg)) {
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            ui_pidIgain->setText(pidReg.getValue());
        }
        return;
    }

    if (reg.name() == "PID_DGAIN") {
        if (m_reglist->contains(reg)) {
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            ui_pidDgain->setText(pidReg.getValue());
        }
    }

    if (reg.name() == "PID_KSTART") {
        if (m_reglist->contains(reg)) {
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            ui_pidKterm->setText(pidReg.getValue());
        }
        return;
    }

    if (reg.name() == "PID_LIMIT_LOW") {
        if (m_reglist->contains(reg)) {
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            ui_pidMin->setText(pidReg.getValue());
        }
        return;
    }

    if (reg.name() == "PID_LIMIT_HIGH") {
        if (m_reglist->contains(reg)) {
            pidReg = m_reglist->at(m_reglist->indexOf(reg));
            ui_pidMax->setText(pidReg.getValue());
        }
        return;
    }
}


void PIDwidget::on_pidEnabled_stateChanged(int state)
{
    //0 = disabled, 1 = enabled for the PID_CTRL register
    // but the checkbox returns "2" when it is checked
    int enabled = 0;
    if (state > 0)
        enabled = 1;
    RegisterDef reg = RegisterDef("PID_CTRL");
    if (m_reglist->contains(reg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        emit writeNeeded(reg.address(), QString::number(enabled));
    }
}

void PIDwidget::on_pidSrc_activated(int index)
{
    //PID_SRC needs to contain the _address_ of the register that contains the source for the PID loop
    // look up what string the combobox index refers to, find it in the register list, write its address
    RegisterDef reg = RegisterDef("PID_SRC");
    RegisterDef targetReg = RegisterDef(ui_pidSrc->itemText(index));
    if (m_reglist->contains(reg) && m_reglist->contains(targetReg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        targetReg = m_reglist->at(m_reglist->indexOf(targetReg));
        emit writeNeeded(reg.address(), QString::number(targetReg.address()));
    }
}

void PIDwidget::on_pidDest_activated(int index)
{
    //PID_DEST needs to contain the _address_ of the register that should take the output from the PID loop
    // look up what string the combobox index refers to, find it in the register list, write its address
    RegisterDef reg = RegisterDef("PID_DEST");
    RegisterDef targetReg = RegisterDef(ui_pidDest->itemText(index));
    if (m_reglist->contains(reg) && m_reglist->contains(targetReg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        targetReg = m_reglist->at(m_reglist->indexOf(targetReg));
        emit writeNeeded(reg.address(), QString::number(targetReg.address()));
    }
}

void PIDwidget::on_pidRef_editingFinished()
{
    QString text = ui_pidRef->text();
    RegisterDef reg = RegisterDef("PID_REF");
    if (m_reglist->contains(reg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        emit writeNeeded(reg.address(), text);
    }
}

void PIDwidget::on_pidMax_editingFinished()
{
    QString text = ui_pidMax->text();
    RegisterDef reg = RegisterDef("PID_LIMIT_HIGH");
    if (m_reglist->contains(reg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        emit writeNeeded(reg.address(), text);
    }
}

void PIDwidget::on_pidMin_editingFinished()
{
    QString text = ui_pidMin->text();
    RegisterDef reg = RegisterDef("PID_LIMIT_LOW");
    if (m_reglist->contains(reg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        emit writeNeeded(reg.address(), text);
    }
}

void PIDwidget::on_pidPgain_editingFinished()
{
    QString text = ui_pidPgain->text();
    RegisterDef reg = RegisterDef("PID_PGAIN");
    if (m_reglist->contains(reg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        emit writeNeeded(reg.address(), text);
    }
}

void PIDwidget::on_pidIgain_editingFinished()
{
    QString text = ui_pidIgain->text();
    RegisterDef reg = RegisterDef("PID_IGAIN");
    if (m_reglist->contains(reg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        emit writeNeeded(reg.address(), text);
    }
}

void PIDwidget::on_pidDgain_editingFinished()
{
    QString text = ui_pidDgain->text();
    RegisterDef reg = RegisterDef("PID_DGAIN");
    if (m_reglist->contains(reg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        emit writeNeeded(reg.address(), text);
    }
}

void PIDwidget::on_pidKterm_editingFinished()
{
    QString text = ui_pidKterm->text();
    RegisterDef reg = RegisterDef("PID_KSTART");
    if (m_reglist->contains(reg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        emit writeNeeded(reg.address(), text);
    }
}

void PIDwidget::on_pidInterval_editingFinished()
{
    QString text = ui_pidInterval->text();
    RegisterDef reg = RegisterDef("PID_PERIOD");
    if (m_reglist->contains(reg)) {
        reg = m_reglist->at(m_reglist->indexOf(reg));
        emit writeNeeded(reg.address(), text);
    }
}
